package com.asialjim.wechat.official.apiservice.impl;

import com.asialjim.wechat.apiservice.ApiRequest;
import com.asialjim.wechat.apiservice.enumeration.WeChatApiReturnEnumeration;
import com.asialjim.wechat.apiservice.exception.WeChatApiException;
import com.asialjim.wechat.core.api.WeChatApiDefaultRes;
import com.asialjim.wechat.core.executor.WeChatApiExecutor;
import com.asialjim.wechat.database.DataBaseService;
import com.asialjim.wechat.official.api.OfficialApi;
import com.asialjim.wechat.official.api.menu.*;
import com.asialjim.wechat.official.apiservice.OfficialMenuService;
import com.asialjim.wechat.official.authpage.AuthPage;
import com.asialjim.wechat.official.authpage.AuthPageService;
import com.asialjim.wechat.official.menu.OfficialMenu;
import com.asialjim.wechat.official.menu.WeChatMenu;
import com.asialjim.wechat.official.menu.WeChatMenuButton;
import com.asialjim.wechat.pojo.account.WeChatAccount;
import com.asialjim.wechat.pojo.account.WeChatAccountHolder;
import com.asialjim.wechat.utils.JsonUtils;
import com.asialjim.wechat.wechataccount.WeChatAccountService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * <h1><em>ASIAL JIM JAVA DOC</em></h1><hr/>
 * <h2>CLASS DESCRIPTION <i>[ NAME: OfficialMenuServiceImpl ]</i></h2><strong>
 * <p> official menu service implements
 * </strong><p><p><em>Copyright &copy; Asial Jim Co., LTD</em><hr/>
 *
 * @author Asial Jim &nbsp;&nbsp; <span>Email:<a href="mailto:asialjim@hotmail.com">asialjim@hotmail.com</a> &nbsp;&nbsp; <a href="asialjim@qq.com">asialjim@qq.com</a></span>
 * @version 1.0.0
 * @since 2021/3/15   &nbsp;&nbsp; JDK 8
 */
@Data
@Slf4j
@AllArgsConstructor
public class OfficialMenuServiceImpl implements OfficialMenuService {
    private final AuthPageService authPageService;
    private final DataBaseService dataBaseService;
    private final WeChatApiExecutor weChatApiExecutor;
    private final WeChatAccountService weChatAccountService;

    @Override
    public AddConditionalMenuApiResponse addConditionalMenu(WeChatMenu menu) {
        log.info("\r\n\tadd conditional menu:{}", menu);
        CreateOfficialMenuApiRequest.CreateMenuBodyParam bodyParam = new CreateOfficialMenuApiRequest.CreateMenuBodyParam(menu);
        AddConditionalMenuApiResponse response = weChatApiExecutor.execute(OfficialApi.ADD_CONDITIONAL_MENU, null, bodyParam, AddConditionalMenuApiResponse.class).parse();
        log.info("\r\n\tadd conditional result:{}", response);
        return response;
    }

    @Override
    public WeChatApiDefaultRes createSelfMenu(WeChatMenu menu) {
        log.info("\r\n\tcreate self menu:{}", menu);
        ApiRequest.RequestBodyParam bodyParam = new CreateOfficialMenuApiRequest.CreateMenuBodyParam(menu);
        ApiRequest request = CreateOfficialMenuApiRequest.builder().requestBodyParam(bodyParam).build();
        WeChatApiDefaultRes response = ((WeChatApiDefaultRes) weChatApiExecutor.execute(OfficialApi.CREATE_MENU, request)).parse();
        log.info("\r\n\tcreate self menu result:{}", response);
        return response;
    }

    @Override
    public WeChatApiDefaultRes deleteConditionalMenu(String menuId) {
        log.info("\r\n\tdelete conditional menu,menu id:{}", menuId);
        DeleteConditionalMenuApiRequest.BodyParam bodyParam = DeleteConditionalMenuApiRequest.BodyParam.builder().menuid(menuId).build();
        WeChatApiDefaultRes response = weChatApiExecutor.execute(OfficialApi.DELETE_CONDITIONAL_MENU, bodyParam, WeChatApiDefaultRes.class).parse();
        log.info("\r\n\tdelete conditional menu result:{}", response);
        return response;
    }

    @Override
    public GetCurrentSelfMenuInfoApiResponse getCurrentSelfMenuInfo() {
        GetCurrentSelfMenuInfoApiResponse response = weChatApiExecutor.execute(OfficialApi.GET_CURRENT_SELF_MENU, GetCurrentSelfMenuInfoApiResponse.class).parse();
        log.info("\r\n\tget self menu info result:{}", response);
        return response;
    }

    @Override
    public GetMenuConfigApiResponse getMenuConfig() {
        GetMenuConfigApiResponse response = weChatApiExecutor.execute(OfficialApi.GET_MENU_CONFIG, GetMenuConfigApiResponse.class).parse();
        log.info("\r\n\tget menu config result:{}", response);
        return response;
    }

    @Override
    public TryMatchConditionalMenuApiResponse tryMatchMenu(String userId) {
        log.info("\r\n\ttry match conditional menu for user:{}", userId);
        TryMatchConditionalMenuApiRequest.BodyParam bodyParam = TryMatchConditionalMenuApiRequest.BodyParam.builder().user_id(userId).build();
        TryMatchConditionalMenuApiResponse response = weChatApiExecutor.execute(OfficialApi.TRY_MATCH_CONDITIONAL_MENU, bodyParam, TryMatchConditionalMenuApiResponse.class).parse();
        log.info("\r\n\ttry conditional menu result:{}", response);
        return response;
    }

    @Override
    public List<OfficialMenu> add(OfficialMenu menu) {
        if (Objects.isNull(menu)) {
            // todo throw an exception
        }

        String appid = StringUtils.isBlank(menu.getAppid()) ? weChatAccountService.currentAppId() : menu.getAppid();
        if (StringUtils.isBlank(appid)) {
            WeChatApiException.cast(WeChatApiReturnEnumeration.CODE_40086);
        }

        // 获取当前公众号所有的菜单
        List<OfficialMenu> menus = Optional.ofNullable(dataBaseService.selectList(OfficialMenu.builder().appid(appid).build())).orElse(new ArrayList<>());
        menus.sort(OfficialMenu::compareTo);
        // 按一级菜单分类
        Map<Integer, List<OfficialMenu>> menuMap = new HashMap<>();
        menus.forEach(item -> {
            List<OfficialMenu> officialMenus = Optional.ofNullable(menuMap.get(item.getFirst())).orElse(new LinkedList<>());
            officialMenus.add(item);
            menuMap.put(item.getFirst(), officialMenus);
        });

        var keys = new LinkedList<Integer>(menuMap.keySet());
        keys.sort(Integer::compareTo);
        Integer first;
        if (Objects.isNull(menu.getFirst())) {
            if (keys.size() > 0) {
                first = keys.get(keys.size() - 1) + 1;
            } else first = 1;
        } else first = menu.getFirst();

        Integer second;
        if (first > 3) {
            WeChatApiException.cast(WeChatApiReturnEnumeration.CODE_40016);
        }

        if (!"parent".equals(menu.getType())) {
            List<OfficialMenu> officialMenus = Optional.ofNullable(menuMap.get(first)).orElse(new ArrayList<>());
            if (officialMenus.size() > 5) {
                WeChatApiException.cast(WeChatApiReturnEnumeration.CODE_40023);
            }
            second = officialMenus.size();
        } else second = 0;

        menu.setFirst(first);
        menu.setSecond(second);
        dataBaseService.insert(menu);
        List<OfficialMenu> officialMenus = dataBaseService.selectList(OfficialMenu.builder().appid(appid).build());
        officialMenus.sort(OfficialMenu::compareTo);
        return officialMenus;
    }

    @Override
    public List<OfficialMenu> addAll(List<OfficialMenu> menus) {
        if (Objects.isNull(menus) || menus.size() == 0) {
            // todo throw an exception
        }
        WeChatAccount weChatAccount = WeChatAccountHolder.get();
        if (Objects.isNull(weChatAccount)) {
            // todo throw an exception
        }

        String appid = weChatAccount.getAppId();
        menus.stream().peek(item -> item.setAppid(appid)).forEach(dataBaseService::insert);
        return menus;
    }

    @Override
    public void create() {
        WeChatAccount weChatAccount = WeChatAccountHolder.get();
        if (Objects.isNull(weChatAccount)) {
            return;
        }

        String appId = weChatAccount.getAppId();
        List<OfficialMenu> officialMenus = dataBaseService.selectList(OfficialMenu.builder().appid(appId).build());

        WeChatMenu menu = weChatMenu(officialMenus);
        log.info("\r\n\tcreate self menu:[{}]", JsonUtils.toJson(menu));
        CreateOfficialMenuApiRequest.CreateMenuBodyParam bodyParam = new CreateOfficialMenuApiRequest.CreateMenuBodyParam(menu);
        WeChatApiDefaultRes response = weChatApiExecutor.execute(OfficialApi.CREATE_MENU, bodyParam, WeChatApiDefaultRes.class);
        log.info("\r\n\tresponse :[{}]", response);
    }

    private WeChatMenu weChatMenu(List<OfficialMenu> officialMenus) {
        officialMenus.sort(OfficialMenu::compareTo);
        WeChatMenu menu = new WeChatMenu();
        Map<Integer, WeChatMenuButton> buttonMap = new HashMap<>();
        officialMenus.stream().filter(item -> "parent".equals(item.getType())).forEach(item -> {
            WeChatMenuButton button = weChatMenuButton(item);
            buttonMap.put(item.getFirst(), button);
        });
        officialMenus.stream().filter(item -> !"parent".equals(item.getType())).forEach(item -> {
            WeChatMenuButton button = weChatMenuButton(item);
            WeChatMenuButton parent = buttonMap.get(item.getFirst());
            List<WeChatMenuButton> subButtons = Optional.ofNullable(parent.getSubButtons()).orElse(new LinkedList<>());
            subButtons.add(button);
            parent.setSubButtons(subButtons);
            buttonMap.put(item.getFirst(), parent);
        });

        List<Integer> keys = new ArrayList<>(buttonMap.keySet());
        keys.sort(Integer::compareTo);
        List<WeChatMenuButton> buttons = new LinkedList<>();
        keys.forEach(key -> buttons.add(buttonMap.get(key)));

        menu.setButton(buttons);
        return menu;
    }

    private WeChatMenuButton weChatMenuButton(OfficialMenu officialMenu) {
        WeChatMenuButton button = new WeChatMenuButton();
        String type = officialMenu.getType();
        switch (type) {
            case "parent":
                break;
            case "view":
                String url = officialMenu.getUrl();
                if (url.startsWith("https://open.wexin.qq.com")) {
                    button.setUrl(url);
                } else {
                    AuthPage authPage = new AuthPage();
                    authPage.setUrl(url);
                    authPage.setAppid(officialMenu.getAppid());
                    authPage.setExpires(-1);
                    authPage = authPageService.addAuthPage(authPage);
                    button.setUrl(authPage.getAuthUrl());
                }
                break;
            case "miniprogram":
                button.setAppId(officialMenu.getMiniAppid());
                button.setPagePath(officialMenu.getPagepath());
                if (officialMenu.getUrl().startsWith("https://open.wexin.qq.com")) {
                    button.setUrl(officialMenu.getUrl());
                } else {
                    AuthPage authPage = new AuthPage();
                    authPage.setUrl(officialMenu.getUrl());
                    authPage.setAppid(officialMenu.getAppid());
                    authPage.setExpires(-1);
                    authPage = authPageService.addAuthPage(authPage);
                    button.setUrl(authPage.getAuthUrl());
                }
                break;
            case "click":
            case "scancode_waitmsg":
            case "scancode_push":
            case "pic_sysphoto":
            case "pic_photo_or_album":
            case "pic_weixin":
            case "location_select":
            case "media_id":
            case "view_limited":
                button.setKey(officialMenu.getKey());
            default:
        }
        if (!"parent".equals(type)) {
            button.setType(type);
        }
        button.setName(officialMenu.getName());
        return button;
    }
}
